Tweaking Spotify For Podcasters Feature Flags
Spotify’s foray into podcasting may be fairly recent, but I’m already discovering some interesting APIs that I can play with. The podcaster dashboard is tremendously useful and offers way more data than Apple and Google combined (with better reliability too), so the more I use it, the more I started thinking that it would be helpful for me to build some kind of automation mechanism to ingest the data into my own storage and then process it outside the default dashboard boundaries. In the process, I also spotted a little thing that I wanted to share with readers, and that is the ability to enable experimental features inside your podcast private view.
The dashboard in question is available at:
https://podcasters.spotify.com/catalog
In on itself, this seems pretty typical - a view that is powered by some backend API, that is populated dynamically whenever the page loads. However, what attracted my curiosity was a specific API call that is executed when the page is first loaded - a GET
request to the following endpoint:
https://generic.wg.spotify.com/podcasters/v0/user/me
This response is authenticated (it uses a bearer token), and returns an interesting response:
{
"authorizations": {
"spotify:show:<SHOW_ID>": [
"viewer",
"editor",
"admin"
]
},
"avatarUrl": "https://i.scdn.co/image/<AVATAR_ID>",
"dataRange": {
"from": "2015-05-01",
"until": "2021-12-28"
},
"displayName": "<USER_NAME>",
"emailAddress": "<USER_EMAIL>",
"employee": false,
"features": {
"s4pod-home-card-playlists": "Release",
"s4pod-home-view-for-all": "Release",
"s4pod-show-episode-v2-button": "Control",
"s4pod-show-localized-content": "Release",
"s4pod-show-site-alert": "Control",
"s4pod-show-wrapped-banners": "Release"
},
"needsToAcceptTerms": false,
"uri": "spotify:user:<USER_NAME>",
"username": "<USER_NAME>"
}
On the surface, this also seems like a standard API response for a user information request. What’s interesting about it, though, is the features
block. Spotify uses it to determine what features to enable for podcast managers in the dashboard based on values in this list. Now, it seems that for most features I am in the Release
group, which means that they are enabled by default. For others, it appears I am in the Control
one. I’d very much like to be in the treatment group to see what those features are about, but that also meant that I needed to intercept and alter the request on the fly before it gets to Spotify. Luckily, I’ve been using a pattern similar to this when I was exploring the Twitter verification API.
The problem with the approach I used earlier is that Spotify chains requests, and I would need to be in stop-and-go traffic to get it to work properly - that’s not really optimal. Fortunately, Fiddler has this amazing capability called AutoResponder. The gist of this feature is that it enables one to automatically respond with pre-baked content to any requests matching a given rule. Because I want to only intercept one request and the JSON body is static, I can use AutoResponder to only send modified JSON data when a match for the URL comes up.
In my case, it looked like this:
The 200_SpotifyJSON.dat
file that I use as a returned value is a response template that is stored in Fiddler’s installation folder (e.g., C:\Users\<YOUR_NAME>\AppData\Local\Programs\Fiddler\ResponseTemplates
) and looks like this snippet:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF8
cache-control: private, max-age=0
access-control-allow-origin: *
access-control-allow-headers: Accept, Authorization, Origin, Content-Type, Spotify-App, Spotify-App-Version, App-Platform, Retry-After, X-Spotify-Connection-Id, SPA-Preferred-Publisher, SPA-Current-Team, X-Cloud-Trace-Context, X-Client-Id, x-twitch-jwt, X-SamsungDaily-Version, X-ClientAttribute-Version, client-token, content-access-token
access-control-allow-methods: GET, POST, OPTIONS, PUT, DELETE, PATCH
access-control-allow-credentials: true
access-control-max-age: 604800
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
date: Thu, 30 Dec 2021 03:24:25 GMT
server: envoy
Via: HTTP/2 edgeproxy, 1.1 google
Alt-Svc: clear
Content-Length: <CONTENT_LENGTH>
<JSON_BODY>
When you first create a response template, make sure to restart Fiddler for it to appear in the list of existing templates.
With this functionality in tow, I was now able to modify one particular response to my liking, and I started by enabling the s4pod-show-episode-v2-button
and s4pod-show-site-alert
features (flagging them as Release
rather than Control
). This resulted in immediate changes to the dashboard:
Clearly, s4pod-show-site-alert
is a note that is being added to the dashboard to alert of data delays. Not that interesting.
What about s4pod-show-episode-v2-button
? From the name, this seems like something that needs to be associated with a podcast episode - that’s our hint as to where to look. I now got a V2 switch available for the Episode Performance metric:
When flipped, it appears that the data is reloaded by exectuting an API call to:
https://generic.wg.spotify.com/podcasters/v0/episodes/<EPISODE_ID>/performance
With the V2 toggle enabled, the request is issued to:
https://generic.wg.spotify.com/podcasters/v0/episodes/<EPISODE_ID>/performance?v2=true
Running these requests through Postman and Fiddler doesn’t show any surface-level differences, but looks like Spotify is experimenting with a potential alternative view over the metric.
The feature flags seem to be interesting from a curiosity perspective, but there was also another one that I overlooked:
"employee": false
What if I set this to true
? Well, it looks like the UI changes a bit to open another view into the world - that of networks:
Notice that in the image above, upon reload with "employee": true
, I now see an extra “from” label that says Spotify Direct Submission. That is a moniker for “submitted the podcast via their own RSS feed” - it’s the Licensor/Hosting Provider from the podcaster dashboard, and it links off to another page:
https://podcasters.spotify.com/network/<NETWORK_ID>
At that point, API calls start failing with a 403 Forbidden
because, naturally, my bearer token does not have the right permissions. However, it does seem like there are APIs to query episode data on a per-network basis, such as:
https://generic.wg.spotify.com/podcasters/v0/networks/<NETWORK_ID>/topEpisodes?start=2021-12-22&end=2021-12-28
https://generic.wg.spotify.com/podcasters/v0/user/networks
The latter returned an empty array, and my catalog became quite empty without network visibility. This concluded my explorations with the podcaster dashboard - there are APIs that I am interested in using, and others that are interesting to learn about just for curiosity’s sake. I genuinely hope that Spotify formalizes a podcasting API surface that will enable developers to do more with their podcast data, especially considering how ahead they are in terms of experience compared to other services.